namespace FIREFLYX {
    /** 后渲染阴影深度汇集成屏幕空间阴影图 着色器 */
    export const BUILTIN_SHADER_POSTEFF_SCREEN_SHADOW = `postEffScreenShadow`;
    DefaultAsset.shaderGenMap[BUILTIN_SHADER_POSTEFF_SCREEN_SHADOW] = () => {
        const vsCode = `#version 300 es
            precision highp float;
            precision highp int;
            layout (location = 0) in vec4 a_Position; //顶点 位置 是有默认值的 (0,0,0,1)
            layout (location = 3) in vec2 a_UV;       //UV 纹理坐标
            out vec2 v_uv;
            void main() {
                v_uv = a_UV;
                vec4 pos = vec4(a_Position.xyz , 1.0);
                gl_Position = pos;
            }
        `;

        const fsCode = `#version 300 es
            #define SHADOWMAP_TYPE_VSM  

            precision highp float;
            precision highp int;
            uniform sampler2D u_mainTex;
            uniform sampler2D u_mainShadowMap_0;
            uniform sampler2D u_mainShadowMap_1;
            uniform sampler2D u_mainShadowMap_2;
            uniform sampler2D u_mainShadowMap_3;
            uniform mat4 u_lightVPMatrix_0;             //转到 主光源VP空间矩阵
            uniform mat4 u_lightVPMatrix_1;             //转到 主光源VP空间矩阵
            uniform mat4 u_lightVPMatrix_2;             //转到 主光源VP空间矩阵
            uniform mat4 u_lightVPMatrix_3;             //转到 主光源VP空间矩阵
            uniform vec4 u_csm_split;                   //级联阴影拆分数据

            uniform vec2 u_resolution;                  //画布分辨率
            uniform ubo_cam_block{
                mat4 u_viewMtx;             //视窗矩阵
                mat4 u_projectMtx;          //投影矩阵
                vec4 u_eyePos;              //眼睛位置
            } uboCam;

            in vec2 v_uv;
            out vec4 color;

            //diefind val
            #define PI2 6.283185307179586   
            #define SHADOW_SAMPLES 8            //阴影采样次数
            #define SHADOW_BIAS 0.02            //阴影偏移
            #define SHADOW_STRIDE 2.0           //阴影偏移的步幅度
            #define POISSON_RINGS 3             //泊松分布圈层数

            vec2 poissonDisk[SHADOW_SAMPLES];

            //用 uv 作为随机种子 计算随机数
            float random(vec2 uv){
                return fract( sin( dot(uv , vec2(12.9898 , 78.233))) * 43758.5453);
            }

            //泊松圆盘采样分布计算
            void poissonDiskSamples( const in vec2 randomSeed ){
                float ANGLE_STEP = PI2 * float(POISSON_RINGS) / float(SHADOW_SAMPLES);
                float INV_SHADOW_SAMPLES = 1.0 / float( SHADOW_SAMPLES );
                
                float angle = random( randomSeed ) * PI2;
                float radius = INV_SHADOW_SAMPLES;
                float radiusStep = radius;

                for( int i=0 ; i < SHADOW_SAMPLES ; i++ ){
                    poissonDisk[i] = vec2(cos( angle ) , sin( angle ) ) * pow( radius , 0.75 );
                    radius += radiusStep;
                    angle += ANGLE_STEP;
                }
            }

            //计算 PCF 软阴影
            float PCFShadow(vec4 wPosLightSpace , sampler2D shadowMap){
                // 执行透视除法
                vec3 projCoords = wPosLightSpace.xyz / wPosLightSpace.w;
                // 变换到[0,1]的范围
                projCoords = projCoords * 0.5 + 0.5;
                // 取得当前片段在光源视角下的深度
                float currentDepth = projCoords.z;
                
                float stride = SHADOW_STRIDE;                               //定义步长
                float shadowMapSize = float(textureSize(shadowMap, 0));     //shadowmap分辨率
                float visibility = 0.0;                                     //初始可见项
                float filterRange = stride / shadowMapSize;                 //滤波窗口的范围

                //泊松圆盘采样得到采样点
                poissonDiskSamples(projCoords.xy);

                //对每个点进行比较深度值并累加
                for(int i = 0; i < SHADOW_SAMPLES; i++){
                    float shadow_depth = texture(shadowMap , projCoords.xy + poissonDisk[i] * filterRange).r;
                    // float res = (currentDepth < shadow_depth) ? 1.0 : 0.0;
                    // float res = (currentDepth < shadow_depth) ? 0.0 : 1.0;
                    visibility += step(shadow_depth , currentDepth - SHADOW_BIAS);
                }

                //返回均值
                float avgVisibility = visibility / float(SHADOW_SAMPLES);
                return avgVisibility;
            }

            vec2 unpackRGBATo2Half(vec4 v) {
                return vec2(v.x + (v.y / 255.0), v.z + (v.w / 255.0));
            }
            
            vec2 texture2DDistribution(sampler2D shadow, vec2 uv) {
                return unpackRGBATo2Half(texture(shadow, uv));
            }

            //获取 VSM
            float VSMShadow(sampler2D shadow, vec2 uv, float compare) {
                float occlusion = 1.0;
                vec2 distribution = texture2DDistribution(shadow, uv);
                // vec2 distribution = texture2D(shadow, uv);
                float hard_shadow = step(compare, distribution.x);
                if(hard_shadow != 1.0) {
                    float distance = compare - distribution.x;
                    float variance = max(0.00000, distribution.y * distribution.y);
                    float softness_probability = variance / (variance + distance * distance);
                    softness_probability = clamp((softness_probability - 0.3) / (0.95 - 0.3), 0.0, 1.0);
                    occlusion = clamp(max(hard_shadow, softness_probability), 0.0, 1.0);
                }
                return 1.0 - occlusion;
            }

            //计算阴影
            float ShadowCalculation(vec4 wPosLightSpace , sampler2D shadowMap , float NoL)
            {
                // 执行透视除法
                vec3 projCoords = wPosLightSpace.xyz / wPosLightSpace.w;
                // 变换到[0,1]的范围
                projCoords = projCoords * 0.5 + 0.5;
                //调节 阴影失真(Shadow Acne) 问题使用 阴影偏移
                float baseBias = SHADOW_BIAS;
                // float bias = max(baseBias * (1.0 - NoL), baseBias * 0.1);  //自适应处理
                float bias = baseBias; 
                // 取得当前片段在光源视角下的深度
                float currentDepth = projCoords.z - bias;
                
                bool isSoft = true;
                
                float shadow = 0.0;
                if(!isSoft){
                    // 取得最近点的深度(使用[0,1]范围下的wPosLightSpace当坐标)
                    float closestDepth = texture(shadowMap, projCoords.xy).r;
                    // 检查当前片段是否在阴影中
                    shadow = step(closestDepth , currentDepth);
                }else{
                    #ifdef SHADOWMAP_TYPE_VSM
                        shadow = VSMShadow(shadowMap , projCoords.xy , currentDepth);
                    #else
                        shadow = PCFShadow(wPosLightSpace , shadowMap);
                    #endif
                }

                //超出 置 0处理 ,解决黑影错误问题
                float shadowMapBoundary = 1.0;
                // shadowMapBoundary = step(currentDepth , 1.0);
                shadowMapBoundary *= step(projCoords.x , 1.0);
                shadowMapBoundary *= step(0.0 , projCoords.x);
                shadowMapBoundary *= step(projCoords.y , 1.0);
                shadowMapBoundary *= step(0.0 , projCoords.y);

                return shadow * shadowMapBoundary;
            }

            void main(){
                mat4 inverseVP = inverse(uboCam.u_projectMtx * uboCam.u_viewMtx);
                vec4 texColor = texture(u_mainTex , v_uv);      //深度纹理
                vec3 screenPosN = vec3(gl_FragCoord.x / u_resolution.x , gl_FragCoord.y / u_resolution.y , texColor.r);
                vec4 ndcPos = vec4(screenPosN * 2.0 - 1.0 , 1.0);
                //VP逆矩阵将NDC坐标转换到世界空间
                vec4 wPos = inverseVP * ndcPos;
                wPos = wPos / wPos.w;                           //将齐次坐标w分量变1得到世界坐标
                wPos.w = 1.0;
                // float Depth = (1.0 - texColor.r) * 500.;     //500 缩放系数是为了方便观察
                // float Depth = 1.0 - texColor.r;
                // color = vec4(Depth,0.0,0.0,1.0);
                // color = vec4(ndcPos.rgb,1.0);                //NDC pos
                // color = vec4(wPos.xyz,1.0);                  //view Pos

                //view 空间坐标
                vec4 vPos = uboCam.u_viewMtx * wPos;
                //级联锥分段值
                float shadowDistance = u_csm_split[3];
                float zDLin = vPos.z / shadowDistance;       
                //计算光源空间世界坐标
                vec4 v_wPosLightSpaces_0 = u_lightVPMatrix_0 * wPos;
                vec4 v_wPosLightSpaces_1 = u_lightVPMatrix_1 * wPos;
                vec4 v_wPosLightSpaces_2 = u_lightVPMatrix_2 * wPos;
                vec4 v_wPosLightSpaces_3 = u_lightVPMatrix_3 * wPos;

                //shadow
                float shadow0 = ShadowCalculation(v_wPosLightSpaces_0 , u_mainShadowMap_0 , 0.0);
                float shadow1 = ShadowCalculation(v_wPosLightSpaces_1 , u_mainShadowMap_1 , 0.0);
                float shadow2 = ShadowCalculation(v_wPosLightSpaces_2 , u_mainShadowMap_2 , 0.0);
                float shadow3 = ShadowCalculation(v_wPosLightSpaces_3 , u_mainShadowMap_3 , 0.0);
                //级联混合
                float csmSpVal0 = step(0.0 , zDLin) * step(zDLin , u_csm_split[0]);
                float csmSpVal1 = step(u_csm_split[0] , zDLin) * step(zDLin , u_csm_split[1]);
                float csmSpVal2 = step(u_csm_split[1] , zDLin) * step(zDLin , u_csm_split[2]);
                float csmSpVal3 = step(u_csm_split[2] , zDLin) * step(zDLin , 1.0);
                shadow0 *= csmSpVal0;
                shadow1 *= csmSpVal1;
                shadow2 *= csmSpVal2;
                shadow3 *= csmSpVal3;
                float shadow = shadow0 + shadow1 + shadow2 + shadow3;
                float shadowAtten = 1.0 - shadow;

                color = vec4(shadowAtten,shadowAtten,shadowAtten,1.0);   //屏幕空间阴影输出
            }
        `;
        //glsl
        const vs = new GLSL();
        vs.setCode(vsCode);
        const fs = new GLSL();
        fs.setCode(fsCode);

        //shader
        let result: Shader = new Shader();
        result.tag = BUILTIN_SHADER_POSTEFF_SCREEN_SHADOW;
        result.lightMode = RenderPipeline.LM_FORWARD;
        result.vertexGLSL = vs;
        result.fragmentGLSL = fs;
        //unifrom 默认值
        result.definePropertyMap = {};

        //apply
        result.apply();

        return result;
    };
}